import json
from Lib.zcm.assistant.ExcelOpts import ExcelOpts
import logging
from Lib.zcm.utils.request_url import RequestURL
from Lib.zcm.utils.format_log import FormatLog
from Lib.constants import *

logger = logging.getLogger()


class SafetyMonitor():
    def __init__(self):
        pass

    def __get_config_content(self, project_id, group_name, file_name, portal_domain_name, retry_time=3):
        url = GET_CONFIGFILE_CONTENT_URL.format(root_url=portal_domain_name, project_id=project_id, fileName=file_name, groupName=group_name)
        if retry_time < 0:
            raise Exception("retry time should be larger than 0")
        headers = HEADERS
        data = RequestURL._request_url_data(url, headers=headers, retry_time=retry_time, method='GET')
        data = json.loads(data[0])
        if data["code"] != SUCCESS_CODE:
            logger.error("Read fail! please check groupName and fileName! groupName:{},file_name{}".format(group_name, file_name))
            return None
        return data["data"]

    def __get_group_info(self, project_id, portal_domain_name):
        url = SELECT_CONFIG_GROUP.format(root_url=portal_domain_name, projectId=project_id)
        return self.__get_data(url)

    def __get_data(self, url):
        headers = HEADERS
        data = RequestURL._request_url_data(url, headers=headers, retry_time=3, method='GET')
        data = json.loads(data[0])
        return data["data"]

    def __get_file_info(self, group_id, portal_domain_name):
        url = SELECT_CONFIG_FILE.format(root_url=portal_domain_name, groupId=group_id)
        return self.__get_data(url)

    def __get_file_content(self, file_id, portal_domain_name):
        url = GET_CONFIG_FILE.format(root_url=portal_domain_name, file_id=file_id)
        return self.__get_data(url)

    def __get_project_id(self, project_name, tenant, portal_domain_name):
        url = GET_PROJECT_ID_URL.format(root_url=portal_domain_name, tenantName=tenant, projectName=project_name)
        headers = HEADERS
        data = RequestURL._request_url_data(url, headers=headers, retry_time=3, method="GET")
        data = json.loads(data[0])
        project_id_list = [i["projectId"] for i in data]
        if len(project_id_list) > 1:
            raise Exception("Get projectid more than one！ projectid: ".format(project_id_list))
        return project_id_list[0]

    def __delete_extra_zero(self, n):
        '''删除小数点后多余的0'''
        if isinstance(n, float):
            n = str(n).rstrip('0')  # 删除小数点后多余的0
            n = n.rstrip('.') if n.endswith('.') else n  # 只剩小数点直接转int，否则转回float
            return n
        return str(n)


    def __scan_properties(self, content, data, group_name, file_name, project_name, tenant, ignore_config):
        """
        扫描properties配置
        ignore_config: {"portalConfig.properties":{"app.security.enable-rbac":"40"}}
        """
        file_ignore_config = dict()
        global_ignore_config = dict()
        if file_name in ignore_config:
            file_ignore_config = ignore_config[file_name]
        if GLOBAL_IGNORE in ignore_config:
            global_ignore_config = ignore_config[GLOBAL_IGNORE]
        content_list = content.split("\n")
        properties_list = []
        # data用户设置的信息
        for rule_info in data:
            key = rule_info[0]
            comment_suggestion = rule_info[1]
            correct_value = rule_info[2]
            correct_value = self.__delete_extra_zero(correct_value)
            config_suggestion = rule_info[3]
            is_exists_flag = False
            # content_list 读取zcm中的信息
            for config_str in content_list:
                config_str = config_str.strip()
                if config_str.find('=') > 0:
                    strs = config_str.split('=')
                    key_length = len(strs[0])
                    strs[0] = strs[0].strip()
                    if strs[0] == key or strs[0] == f'#{key}':
                        is_exists_flag = True
                        strs[1] = config_str[key_length + 1:].strip()
                        if strs[0].startswith("#"):
                            properties_list.append({
                                "项目": project_name,
                                "租户": tenant,
                                "配置组": group_name,
                                "配置文件名": file_name,
                                "配置项": strs[0][1:],
                                "是否注释": "是",
                                "注释建议": comment_suggestion,
                                "正确配置": "无",
                                "当前配置": "无",
                                "是否存在风险": "是",
                                "配置建议": "无"
                            })
                        else:
                            correct_value_list = [correct_value]
                            if file_ignore_config.get(strs[0], None):
                                correct_value_list.append(file_ignore_config[strs[0]])
                            if global_ignore_config.get(strs[0], None):
                                correct_value_list.append(global_ignore_config[strs[0]])
                            if strs[1] not in correct_value_list:
                                properties_list.append({
                                    "项目": project_name,
                                    "租户": tenant,
                                    "配置组": group_name,
                                    "配置文件名": file_name,
                                    "配置项": strs[0],
                                    "是否注释": "否",
                                    "注释建议": "无",
                                    "正确配置": correct_value,
                                    "当前配置": strs[1],
                                    "是否存在风险": "是",
                                    "配置建议": config_suggestion
                                })
                else:
                    if config_str.find(key) >= 0:
                        config_str = config_str.strip()
                        if config_str == key:
                            is_exists_flag = True
                            properties_list.append({
                                "项目": project_name,
                                "租户": tenant,
                                "配置组": group_name,
                                "配置文件名": file_name,
                                "配置项": key,
                                "是否注释": "否",
                                "注释建议": "无",
                                "正确配置": correct_value,
                                "当前配置": '无',
                                "是否存在风险": "是",
                                "配置建议": config_suggestion
                            })
            if not is_exists_flag:
                # 配置文件中不存在用户所要扫描的key
                properties_list.append(
                    {
                        "项目": project_name,
                        "租户": tenant,
                        "配置组": group_name,
                        "配置文件名": file_name,
                        "配置项": key,
                        "是否注释": "否",
                        "注释建议": "无",
                        "正确配置": correct_value,
                        "当前配置": '无',
                        "是否存在风险": "是",
                        "配置建议": "配置项不存在！"
                    }
                )

        return properties_list


    def check_zcm_config_by_parms(self, rule_info, portal_domain_name, project_id):
        config_rule_info = {
            rule_info["group_name"]:
                {
                    rule_info["file_name"]: [[rule_info["key_name"], rule_info["comment_suggestion"], rule_info["correct_value"], rule_info["config_suggestion"]]]
                }
        }
        ignore_config = rule_info.get("ignore_config", dict())
        self.__check_config_by_config_rule_info(project_id, rule_info["project_name"], rule_info["tenant"], portal_domain_name, config_rule_info, ignore_config)


    def __check_config_by_config_rule_info(self, project_id, project_name, tenant, portal_domain_name, config_rule_info, ignore_config):
        if project_id:
            try:
                project_id = int(project_id)
            except Exception:
                raise Exception("project_id: an integer is required！")
        else:
            project_id = self.__get_project_id(project_name, tenant, portal_domain_name)
        config_result_list = []
        for group_name in config_rule_info:
            file_info = config_rule_info[group_name]
            for file_name in file_info:
                content = self.__get_config_content(project_id, group_name, file_name, portal_domain_name)
                if content is not None:
                    if file_name.endswith(".properties"):
                        properties_list = self.__scan_properties(content, file_info[file_name], group_name, file_name, project_name, tenant, ignore_config)
                        config_result_list += properties_list
        if config_result_list:
            format_log = FormatLog(config_result_list)
            logger.info(format_log.transform())
            raise Exception("The configuration does not match the rules, please check!")


    def check_zcm_config(self, rule_info, file_path, portal_domain_name, project_id):
        if not all([rule_info.get('project_name', None), rule_info.get('tenant', None)]):
            raise Exception('Missing parameter! please check project_name and tenant!')
        if file_path:
            # 传入excel，则扫描excel中的配置项，不扫描参数传入的配置项
            return self.check_zcm_config_by_excel(rule_info, file_path, portal_domain_name, project_id)
        else:
            # 不传入excel时，则扫描参数传入的配置项
            return self.check_zcm_config_by_parms(rule_info, portal_domain_name, project_id)


    def check_zcm_config_by_excel(self, rule_info, file_path, portal_domain_name, project_id):
        project_name = rule_info["project_name"]
        tenant = rule_info["tenant"]
        ignore_config = rule_info.get("ignore_config", dict())
        if portal_domain_name != ZCM_PORTAL_ROOT_URL:
            if not portal_domain_name.endswith("/"):
                portal_domain_name += "/"
        excel_obj = ExcelOpts(file_path)
        config_rule_info = excel_obj.read_config_rule()
        self.__check_config_by_config_rule_info(project_id, project_name, tenant, portal_domain_name, config_rule_info, ignore_config)


    def check_zcm_config_pwd(self, project_name, tenant, ignore_config, portal_domain_name, project_id):
        assert type(ignore_config) == dict, "ignore_config must be a dict!"
        if portal_domain_name != ZCM_PORTAL_ROOT_URL:
            if not portal_domain_name.endswith("/"):
                portal_domain_name += "/"
        if project_id:
            try:
                project_id = int(project_id)
            except Exception:
                raise Exception("project_id: an integer is required！")
        else:
            project_id = self.__get_project_id(project_name, tenant, portal_domain_name)
        pwd_result = []
        group_infos = self.__get_group_info(project_id, portal_domain_name)
        for group_info in group_infos:
            file_infos = self.__get_file_info(group_info["groupId"], portal_domain_name)
            for file_info in file_infos:
                if file_info["fileName"].endswith(".properties"):
                    file_content = self.__get_file_content(file_info["fileConfigId"], portal_domain_name)
                    content = file_content["content"]
                    properties_pwd_result = self.__check_pwd(content, project_name, tenant, group_info["groupName"], file_info["fileName"], ignore_config)
                    pwd_result += properties_pwd_result
        if pwd_result:
            format_log = FormatLog(pwd_result)
            logger.info(format_log.transform())
            raise Exception("Unencrypted password exists in the password configuration, please check!")


    def __check_pwd_key(self, key):
        for key_rule in PWD_CHOICE:
            if key.find(key_rule) >= 0:
                return True
        return False


    def __check_pwd(self, content, project_name, tenant, group_name, file_name, ignore_config):
        # ignore = {
        # 	"文件名":["配置项", "配置项", "配置项"],
        # 	"文件名":["配置项", "配置项", "配置项"],
        # }
        file_ignore_config = []
        if file_name in ignore_config:
            file_ignore_config += ignore_config[file_name]
        pwd_result = []
        line_list = content.split("\n")
        for line in line_list:
            line = line.strip()
            if not line.startswith("#") and line.find("=") > 0:
                strs = line.split("=")
                strs[1] = line[len(strs[0]) + 1:].strip()
                strs[0] = strs[0].strip()

                key = strs[0]
                value = strs[1]
                if not self.__check_pwd_key(key):
                    continue
                if value.find(PWD_RULE) < 0 and key not in file_ignore_config:
                    tmp_dict = {
                        "项目": project_name,
                        "租户": tenant,
                        "配置组": group_name,
                        "配置文件名": file_name,
                        "配置项": key,
                        # "当前配置": value,
                        "是否加密": "否",
                        "修复建议": PWD_SUGGESTION,
                    }
                    if not value:
                        tmp_dict["修复建议"] = PWD_NO_VALUE_SUGGESTION
                    pwd_result.append(tmp_dict)
        return pwd_result

if __name__ == '__main__':
    SafetyMonitor().check_zcm_config("iCICD研发管理", "南研小机房", "D:\\faq\\wangliqin\\rule.xlsx")



















